# -*- coding: utf-8 -*-
from google.appengine.ext import webapp
from google.appengine.ext import db
from google.appengine.api import memcache

import random
import pickle

from datetime import datetime
from datetime import timedelta
from Cookie import BaseCookie


__all__ = ['session', 'session2']

def _generate_id(length=64):
    '''Generate a session id. '''
    s = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_'
    a = []
    for i in range(length):
        a.append(random.choice(s))
    return ''.join(a)


class ResponseWithCookie:
    def __init__(self, response):
        self.__response = response
    
    def set_cookie(self, key, value='', max_age=None,
                   path='/', domain=None, secure=None, httponly=False,
                   version=None, comment=None):
        cookies = BaseCookie()
        cookies[key] = value
        for var_name, var_value in [
            ('max-age', max_age),
            ('path', path),
            ('domain', domain),
            ('secure', secure),
            ('HttpOnly', httponly),
            ('version', version),
            ('comment', comment),
            ]:
            if var_value is not None and var_value is not False:
                cookies[key][var_name] = str(var_value)
        header_value = cookies[key].output(header='').lstrip()
        self.headers.add_header('Set-Cookie', header_value)
    
    def delete_cookie(self, key):
        self.set_cookie(key, '', path=path, domain=domain, max_age=0)
        
    def __getattr__(self, name):
        return getattr(self.__response, name)


class Session(dict):

    SESSION_ID_NAME = 'sessionid'

    def __init__(self, request, response, expire):
        self._request = request
        self._response = response
        self.expire = expire
        self._cookie = BaseCookie()
        try:
            self._session_id = self._request.cookies[Session.SESSION_ID_NAME]
            dict.__init__(self, memcache.get(self.session_id))
        except:
            self._session_id = _generate_id()
            dict.__init__(self)
        self.__destroyed = False
        self.__is_active = False

    def __getitem__(self, key):
        self.__is_active = True
        return dict.__getitem__(self, key)

    def __setitem__(self, key, value):
        self.__is_active = True
        dict.__setitem__(self, key, value)

    def __delitem__(self, key):
        self.__is_active = True
        dict.__delitem__(self, key)

    def destroy(self):
        self.clear()
        self.__destroyed = True

    @property
    def session_id(self):
        return self._session_id

    def commit(self):
        sname = Session.SESSION_ID_NAME
        if self.__destroyed:
            memcache.delete(self._session_id)
            self._cookie[sname]=''
            self._cookie[sname]['expires']=-9999999
            self.__destroyed = False
        elif self.__is_active:
            memcache.set(self._session_id, dict(self), self.expire.days*3600*24+self.expire.seconds)
            self._cookie[sname] = self.session_id
            self._cookie[sname]['expires'] = self.expire.days*3600*24+self.expire.seconds
            self.__is_active = False
        
        if sname in self._cookie:
            self._response.headers.add_header('Set-Cookie', self._cookie[sname].output(header='').lstrip())
        


class SessionModel(db.Model):
    data = db.BlobProperty()
    expire = db.DateTimeProperty(auto_now_add=True)
    #max_alive = db.DateTimeProperty(auto_now_add=True)


class Session2(dict):

    def __init__(self, request, response, expire):
        self._request = request
        self._response = response
        self.expire = expire
        self._cookie = BaseCookie()
        try:
            self._session_id = self._request.cookies[Session.SESSION_ID_NAME]
            self._session_model = SessionModel.get_by_key_name(self._session_id)
            if self._session_model == None:
                raise Exception
            else:
                dict.__init__(self, pickle.loads(self._session_model.data))
        except:
            self._session_id = _generate_id()
            self._session_model = SessionModel(data=pickle.dumps({}), key_name=self._session_id)
            dict.__init__(self)
        self.__destroyed = False
        self.__is_active = False
        
    def __getitem__(self, key):
        self.__is_active = True
        return dict.__getitem__(self, key)

    def __setitem__(self, key, value):
        self.__is_active = True
        dict.__setitem__(self, key, value)

    def __delitem__(self, key):
        self.__is_active = True
        dict.__delitem__(self, key)

    def destroy(self):
        self.clear()
        self.__destroyed = True

    @property
    def session_id(self):
        return self._session_id

    def cleanup(self):
        """Clean up the expired data."""
        sessions = SessionModel.all().filter('expire <=', datetime.utcnow()).fetch(1000)
        for sess in sessions:
            sess.delete()
            
    def commit(self):
        sname = Session.SESSION_ID_NAME
        if self.__destroyed:
            SessionModel.get_by_key_name(self._session_id).delete()
            self._cookie[sname]=''
            self._cookie[sname]['expires']=-9999999
            self.__destroyed = False
        elif self.__is_active:
            expire = datetime.utcnow()+self.expire
            self._session_model.data = pickle.dumps(dict(self))
            self._session_model.expire = expire
            self._session_model.put()
            self._cookie[sname] = self._session_id
            self._cookie[sname]['expires'] = self.expire.days*3600*24+self.expire.seconds
            self.__is_active = False
        if sname in self._cookie:
            self._response.headers.add_header('Set-Cookie', self._cookie[sname].output(header='').lstrip())
        
    def __del__(self):
        self.cleanup()

def session(expire=timedelta(minutes=30)):
    def _decorator(handler_method):
        def _warper(self, **args):
            self.session = Session(self.request, self.response, expire)
            handler_method(self, *args)
            self.session.commit()
        return _warper
    return _decorator

def session2(expire=timedelta(minutes=30)):
    def _decorator(handler_method):
        def _warper(self, **args):
            self.session = Session2(self.request, self.response, expire)
            handler_method(self, *args)
            self.session.commit()
        return _warper
    return _decorator
